/**
 * @file enc28j60.c
 * @author zdk
 * @brief ENC28J60驱动
 * @version 0.1
 * @date 2020-06-01 初始版本
 * 
 * BSD 3-Clause License
 * 
 * Copyright (c) 2021, water_zhang
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * 
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * 
 * * Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * 
 */

#include "enc28j60.h"

//默认提供的MAC地址
uint8_t default_mac_addr[6] = {0xCC, 0x8A, 0xAC, 0x74, 0x4C, 0x65};

//调试用的断言，如果不需要断言，清空即可
#define ASSERT_PARAM(_x_)

/***************SPI接口与硬件IO相关函数，根据实际硬件平台修改，此处为stm32的HAL库**************/
#include "stm32f4xx.h"

#define SPI_PORT GPIOA
#define SPI_CLK_PIN GPIO_PIN_5
#define SPI_MISO_PIN GPIO_PIN_6
#define SPI_MOSI_PIN GPIO_PIN_7
#define SPI_CS_PIN GPIO_PIN_15

SPI_HandleTypeDef hspi1;

//成功返回1，失败返回0
static uint8_t __ENC28J60_SPI_Init(void)
{
    hspi1.Instance = SPI1;
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    hspi1.Init.CRCPolynomial = 10;
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
    hspi1.Init.Mode = SPI_MODE_MASTER;
    hspi1.Init.NSS = SPI_NSS_SOFT;
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;

    __HAL_RCC_SPI1_CLK_ENABLE();
    if (HAL_SPI_Init(&hspi1) != HAL_OK)
    {
        return 0;
    }
    return 1;
}

static void __ENC28J60_PIN_INIT(void)
{
    GPIO_InitTypeDef init;

    __HAL_RCC_GPIOA_CLK_ENABLE();

    /* 初始化SPI相关引脚 */
    init.Alternate = GPIO_AF5_SPI1;
    init.Mode = GPIO_MODE_AF_PP;
    init.Pin = SPI_MOSI_PIN | SPI_CLK_PIN | SPI_MISO_PIN;
    init.Pull = GPIO_NOPULL;
    init.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    HAL_GPIO_Init(SPI_PORT, &init);

    /* 初始化片选信号对应引脚 */
    HAL_GPIO_WritePin(SPI_PORT, SPI_CS_PIN, GPIO_PIN_SET);
    init.Mode = GPIO_MODE_OUTPUT_PP;
    init.Pin = SPI_CS_PIN;
    init.Pull = GPIO_NOPULL;
    init.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(SPI_PORT, &init);
}

//1=高电平，0=低电平
static void __ENC28J60_SPI_NSS(uint8_t level)
{
    //检查一下，level只能是0或1
    ASSERT_PARAM(!(level & ~0x01));
    HAL_GPIO_WritePin(SPI_PORT, SPI_CS_PIN, (GPIO_PinState)level);
}

//发送一字节数据
static void __ENC28J60_SPI_Transmit(uint8_t *pData)
{
    ASSERT_PARAM(pData);
    HAL_SPI_Transmit(&hspi1, pData, 1, HAL_MAX_DELAY);
}

//接收一字节数据
static void __ENC28J60_SPI_Receive(uint8_t *pData)
{
    ASSERT_PARAM(pData);
    HAL_SPI_Receive(&hspi1, pData, 1, HAL_MAX_DELAY);
}
/*************************************************************************************/

/*************ENC28J60指令集函数接口**************************************************/

//读控制寄存器（用于ETH寄存器组）
void __ENC28J60_RCR_ETH(uint8_t addr, uint8_t *pData)
{
    uint8_t tmp = 0x00 | addr;

    ASSERT_PARAM(pData);

    __ENC28J60_SPI_NSS(0);
    __ENC28J60_SPI_Transmit(&tmp);
    __ENC28J60_SPI_Receive(pData);
    __ENC28J60_SPI_NSS(1);
}

//读控制寄存器（用于MAC与MII寄存器组）
void __ENC28J60_RCR_MAC_MII(uint8_t addr, uint8_t *pData)
{
    uint8_t tmp = 0x00 | addr;

    ASSERT_PARAM(pData);

    __ENC28J60_SPI_NSS(0);
    __ENC28J60_SPI_Transmit(&tmp);
    __ENC28J60_SPI_Receive(pData); //无效字节
    __ENC28J60_SPI_Receive(pData);
    __ENC28J60_SPI_NSS(1);
}

//读缓冲器
static void __ENC28J60_RBM(uint8_t *pData, uint16_t len)
{
    uint8_t tmp = 0x3A;

    ASSERT_PARAM(pData);

    __ENC28J60_SPI_NSS(0);
    __ENC28J60_SPI_Transmit(&tmp);
    for (int i = 0; i < len; i++)
    {
        __ENC28J60_SPI_Receive(pData + i);
    }
    __ENC28J60_SPI_NSS(1);
}

//读缓冲器，但是丢弃读到的数据
static void __ENC28J60_RBM_DISCARD(uint16_t len)
{
    uint8_t tmp = 0x3A;
    uint8_t data = 0x00;

    __ENC28J60_SPI_NSS(0);
    __ENC28J60_SPI_Transmit(&tmp);
    for (int i = 0; i < len; i++)
    {
        __ENC28J60_SPI_Receive(&data);
    }
    __ENC28J60_SPI_NSS(1);
}

//写控制寄存器
void __ENC28J60_WCR(uint8_t addr, uint8_t *pData)
{
    uint8_t tmp = (0x02 << 5) | addr;

    ASSERT_PARAM(pData);

    __ENC28J60_SPI_NSS(0);
    __ENC28J60_SPI_Transmit(&tmp);
    __ENC28J60_SPI_Transmit(pData);
    __ENC28J60_SPI_NSS(1);
}

//写缓冲器
static void __ENC28J60_WBM(uint8_t *pData, uint16_t len)
{
    uint8_t tmp = 0x7A;

    ASSERT_PARAM(pData);

    __ENC28J60_SPI_NSS(0);
    __ENC28J60_SPI_Transmit(&tmp);
    for (int i = 0; i < len; i++)
    {
        __ENC28J60_SPI_Transmit(pData + i);
    }
    __ENC28J60_SPI_NSS(1);
}

//位域置1，只用于ETH寄存器组
void __ENC28J60_BFS(uint8_t addr, uint8_t mask)
{
    uint8_t tmp = (0x04 << 5) | addr;

    __ENC28J60_SPI_NSS(0);
    __ENC28J60_SPI_Transmit(&tmp);
    __ENC28J60_SPI_Transmit(&mask);
    __ENC28J60_SPI_NSS(1);
}

//位域清0，只用于ETH寄存器组
void __ENC28J60_BFC(uint8_t addr, uint8_t mask)
{
    uint8_t tmp = (0x05 << 5) | addr;

    __ENC28J60_SPI_NSS(0);
    __ENC28J60_SPI_Transmit(&tmp);
    __ENC28J60_SPI_Transmit(&mask);
    __ENC28J60_SPI_NSS(1);
}

//切换寄存器BANK，BANK0~BANK3
void __ENC28J60_SET_BANK(uint8_t bank)
{
    __ENC28J60_BFC(REG_NOBANK_ECON1, 0x03);
    __ENC28J60_BFS(REG_NOBANK_ECON1, bank);
}

//系统命令（软复位）
static void __ENC28J60_SC(void)
{
    uint8_t tmp = 0xFF;

    __ENC28J60_SPI_NSS(0);
    __ENC28J60_SPI_Transmit(&tmp);
    __ENC28J60_SPI_NSS(1);
}

/*************************************************************************************/

/**
 * @brief 软复位，之后需要重新初始化
 * 
 */
static void ENC28J60_SoftReset(void)
{
    __ENC28J60_SC();
}

/**
 * @brief 初始化ENC28J60
 * 
 * @param mac_addr 提供的MAC6字节数组，高字节在前，6字节
 * @return uint8_t，0=初始化失败，1=初始化成功
 */
uint8_t ENC28J60_Init(uint8_t *mac_addr)
{
    uint8_t tmp = 0;
    uint8_t rec_buffer_start_l = (uint8_t)(ENC28J60_RECEIVE_BUFFER_START & 0x00FF);
    uint8_t rec_buffer_start_h = (uint8_t)((ENC28J60_RECEIVE_BUFFER_START >> 8) & 0x00FF);

    ASSERT_PARAM(mac_addr);

    /* 引脚初始化 */
    __ENC28J60_PIN_INIT();
    if (!__ENC28J60_SPI_Init())
    {
        return 0;
    }

    /* 复位网卡 */
    ENC28J60_SoftReset();

    /*失能数据包接收与发送*/
    tmp = 0xC0;
    __ENC28J60_WCR(REG_NOBANK_ECON1, &tmp);

    /*初始化缓冲器*/
    __ENC28J60_SET_BANK(0);
    __ENC28J60_WCR(REG_BANK0_ERXSTL, &rec_buffer_start_l);
    __ENC28J60_WCR(REG_BANK0_ERXSTH, &rec_buffer_start_h);
    __ENC28J60_WCR(REG_BANK0_ERXRDPTL, &rec_buffer_start_l);
    __ENC28J60_WCR(REG_BANK0_ERXRDPTH, &rec_buffer_start_h);

    /*设置过滤器为接收所有数据包*/
    __ENC28J60_SET_BANK(1);
    tmp = 0x00;
    __ENC28J60_WCR(REG_BANK1_ERXFCON, &tmp);

    /*查询CLKRDY等待时钟稳定*/
    uint8_t cnt = 0;
    uint8_t clk_ready = 0;
    while (!clk_ready)
    {
        uint8_t tmp = 0x00;
        __ENC28J60_RCR_ETH(REG_NOBANK_ESTAT, &tmp);
        clk_ready = (tmp & 0x01) ? 1 : 0;
        cnt++;
        if (cnt > 100)
        {
            return 0;
        }
    }

    /*设置MAC层*/
    __ENC28J60_SET_BANK(2);
#if ENC28J60_ENABLE_FULL_DUPLEX
    tmp = 0x05; //半双工
#else
    tmp = 0x0D; //全双工
#endif
    __ENC28J60_WCR(REG_BANK2_MACON1, &tmp);
    tmp = 0x00;
    __ENC28J60_WCR(REG_BANK2_MACON2, &tmp);
#if ENC28J60_ENABLE_FULL_DUPLEX
    tmp = 0x32; //半双工
#else
    tmp = 0x33; //全双工
#endif
    __ENC28J60_WCR(REG_BANK2_MACON3, &tmp);
    tmp = ENC28J60_PACKAGE_MAX_LEN & 0x00FF;
    __ENC28J60_WCR(REG_BANK2_MAMXFLL, &tmp);
    tmp = (ENC28J60_PACKAGE_MAX_LEN >> 8) & 0x00FF;
    __ENC28J60_WCR(REG_BANK2_MAMXFLH, &tmp);
#if ENC28J60_ENABLE_FULL_DUPLEX
    tmp = 0x12; //半双工
#else
    tmp = 0x15; //全双工
#endif
    __ENC28J60_WCR(REG_BANK2_MABBIPG, &tmp);
    tmp = 0x12;
    __ENC28J60_WCR(REG_BANK2_MAIPGL, &tmp);
#if !ENC28J60_ENABLE_FULL_DUPLEX
    tmp = 0x0C;
    __ENC28J60_WCR(REG_BANK2_MAIPGH, &tmp);
#endif

    /*设置MAC地址*/
    __ENC28J60_SET_BANK(3);
    tmp = mac_addr[0];
    __ENC28J60_WCR(REG_BANK3_MAADR5, &tmp);
    tmp = mac_addr[1];
    __ENC28J60_WCR(REG_BANK3_MAADR4, &tmp);
    tmp = mac_addr[2];
    __ENC28J60_WCR(REG_BANK3_MAADR3, &tmp);
    tmp = mac_addr[3];
    __ENC28J60_WCR(REG_BANK3_MAADR2, &tmp);
    tmp = mac_addr[4];
    __ENC28J60_WCR(REG_BANK3_MAADR1, &tmp);
    tmp = mac_addr[5];
    __ENC28J60_WCR(REG_BANK3_MAADR0, &tmp);

    //清空中断标志位寄存器，默认不打开中断
    __ENC28J60_SET_BANK(0);
    __ENC28J60_BFC(REG_NOBANK_ESTAT, 0xFE); //最低位为时钟就绪位，不清零
    __ENC28J60_BFC(REG_NOBANK_EIR, 0xFF);
    __ENC28J60_BFC(REG_NOBANK_EIE, 0xFF);

    //使能数据包接收与发送
    tmp = 0x04;
    __ENC28J60_WCR(REG_NOBANK_ECON1, &tmp);

    return 1;
}

/**
 * @brief 使能MAC层的环回模式，一般用于调试
 * 
 */
void ENC28J60_MACLoopBackEnable(void)
{
    uint8_t tmp = 0;

    __ENC28J60_SET_BANK(2);
    __ENC28J60_RCR_MAC_MII(REG_BANK2_MACON1, &tmp);
    tmp |= 0x10;
    __ENC28J60_WCR(REG_BANK2_MACON1, &tmp);
}

/**
 * @brief 禁用MAC层的环回模式，一般用于调试
 * 
 */
void ENC28J60_MACLoopBackDisable(void)
{
    uint8_t tmp = 0;

    __ENC28J60_SET_BANK(2);
    __ENC28J60_RCR_MAC_MII(REG_BANK2_MACON1, &tmp);
    tmp &= ~0x10;
    __ENC28J60_WCR(REG_BANK2_MACON1, &tmp);
}

/**
 * @brief 发送数据包，数据包长度不得长于ENC28J60_PACKAGE_MAX_LEN
 * @note 包结构为：目标地址（6bytes）+ 源地址（6bytes）+ 包长/类型（2bytes）+ 数据（最大1500bytes）
 * 
 * @param pBuff 数据包缓冲区
 * @param len 数据包长度
 * @return uint8_t，2=网卡繁忙（正在发送上一次的数据包），1=发送成功，0=发送失败
 */
uint8_t ENC28J60_SendPacket(uint8_t *pBuff, uint16_t len)
{
    uint8_t tmp = 0;

    ASSERT_PARAM(pBuff);

    //检查包长是否超出最大值，其中4字节要预留给自动生成的32bit-CRC校验
    if (len > ENC28J60_PACKAGE_MAX_LEN - 4)
    {
        return 0;
    }

    //检查网卡是否繁忙
    __ENC28J60_RCR_ETH(REG_NOBANK_ECON1, &tmp);
    if (tmp & 0x08)
    {
        return 2;
    }

    //生成包控制字节
    uint8_t packet_ctr_byte = 0x00;

    //编程ETXST指定包头位置
    __ENC28J60_SET_BANK(0);
    tmp = ENC28J60_TRANSMIT_BUFFER_START & 0xFF;
    __ENC28J60_WCR(REG_BANK0_ETXSTL, &tmp);
    tmp = (ENC28J60_TRANSMIT_BUFFER_START >> 8) & 0xFF;
    __ENC28J60_WCR(REG_BANK0_ETXSTH, &tmp);

    //设置缓冲器写指针
    tmp = ENC28J60_TRANSMIT_BUFFER_START & 0xFF;
    __ENC28J60_WCR(REG_BANK0_EWRPTL, &tmp);
    tmp = (ENC28J60_TRANSMIT_BUFFER_START >> 8) & 0xFF;
    __ENC28J60_WCR(REG_BANK0_EWRPTH, &tmp);

    //写入包控制字节与数据包到缓存
    __ENC28J60_WBM(&packet_ctr_byte, 1);
    __ENC28J60_WBM(pBuff, len);

    //编程ETXND指定包尾位置（1字节控制字节+包数据长度，指向最后一个字节）
    tmp = (ENC28J60_TRANSMIT_BUFFER_START + len + 1 - 1) & 0xFF;
    __ENC28J60_WCR(REG_BANK0_ETXNDL, &tmp);
    tmp = ((ENC28J60_TRANSMIT_BUFFER_START + len + 1 - 1) >> 8) & 0xFF;
    __ENC28J60_WCR(REG_BANK0_ETXNDH, &tmp);

    //清TXIF位，置TXRTS位为1开始发送
    __ENC28J60_BFC(REG_NOBANK_EIR, 0x08);
    __ENC28J60_BFS(REG_NOBANK_ECON1, 0x08);

    return 1;
}

/**
 * @brief 读取网卡缓冲区中的一个数据包
 * @note 数据包格式：目标地址（6bytes）+ 源地址（6bytes）+ 包长/类型（2bytes）+ 数据（最大1500bytes）+ 填充（可选）+ CRC（4Bytes）
 * 
 * @param pBuff 缓冲区
 * @param max_len 缓冲区最大字节数
 * @return uint16_t 读到的字节数，如果缓冲区中没有数据包，返回0；如果提供的缓冲空间不足存下完整包，返回1
 */
uint16_t ENC28J60_ReceivePacket(uint8_t *pBuff, uint16_t max_len)
{
    //查询缓冲区的数据包个数
    uint8_t packet_cnt = 0;
    __ENC28J60_SET_BANK(1);
    __ENC28J60_RCR_ETH(REG_BANK1_EPKTCNT, &packet_cnt);

    ASSERT_PARAM(pBuff);

    //有完整数据包就读出来，没有直接退出
    if (packet_cnt > 0)
    {
        //获取当前接收数据包读指针
        uint8_t read_ptr_l = 0;
        uint8_t read_ptr_h = 0;
        __ENC28J60_SET_BANK(0);
        __ENC28J60_RCR_ETH(REG_BANK0_ERXRDPTL, &read_ptr_l);
        __ENC28J60_RCR_ETH(REG_BANK0_ERXRDPTH, &read_ptr_h);

        //设置缓冲区读指针
        __ENC28J60_WCR(REG_BANK0_ERDPTL, &read_ptr_l);
        __ENC28J60_WCR(REG_BANK0_ERDPTH, &read_ptr_h);

        //读出6字节报头
        uint8_t packet_header_buffer[6] = {0};
        __ENC28J60_RBM(packet_header_buffer, 6);

        //根据报头中的数据包长度读数据包
        uint16_t packet_len = (((uint16_t)(packet_header_buffer[3])) << 8) + packet_header_buffer[2];
        if (packet_len > max_len)
        {
            //读出这些无效数据然后丢弃
            __ENC28J60_RBM_DISCARD(packet_len);

            //移动ERXRDPT来释放已读取数据包的缓冲空间
            __ENC28J60_WCR(REG_BANK0_ERXRDPTL, &(packet_header_buffer[0]));
            __ENC28J60_WCR(REG_BANK0_ERXRDPTH, &(packet_header_buffer[1]));

            //向PKTDEC写1使得数据包计数EPKCNT自减1
            __ENC28J60_BFS(REG_NOBANK_ECON2, 0x40);
            return 1;
        }
        __ENC28J60_RBM(pBuff, packet_len);

        //移动ERXRDPT来释放已读取数据包的缓冲空间
        __ENC28J60_WCR(REG_BANK0_ERXRDPTL, &(packet_header_buffer[0]));
        __ENC28J60_WCR(REG_BANK0_ERXRDPTH, &(packet_header_buffer[1]));

        //向PKTDEC写1使得数据包计数EPKCNT自减1
        __ENC28J60_BFS(REG_NOBANK_ECON2, 0x40);

        return packet_len;
    }
    else
    {
        return 0;
    }
}

#if ENC28J60_ENABLE_INT

/**
 * @brief 使能INT引脚的中断输出功能
 * 
 * @param flag 要使能的中断，可以为如下值相或，或者0xFF打开所有中断：
 *              EIE_FLAG_PKT     接收数据包待处理中断允许位 
 *              EIE_FLAG_DMA     DMA 中断允许位 
 *              EIE_FLAG_LINK    连接状态改变中断允许位 
 *              EIE_FLAG_TX      发送中断允许位 
 *              EIE_FLAG_WOL     WOL 中断允许位 
 *              EIE_FLAG_TXER    发送错误中断允许位 
 *              EIE_FLAG_RXER    接收错误中断允许位 
 */
void ENC28J60_EnableINT(uint8_t flag)
{
    __ENC28J60_BFC(REG_NOBANK_EIR, flag);
    __ENC28J60_BFS(REG_NOBANK_EIE, flag | EIE_FLAG_INTI);
}

/**
 * @brief 失能INT引脚的中断输出功能
 * 
 * @param flag 要失能的中断，可以为如下值相或，或者0xFF关闭所有中断：
 *              EIE_FLAG_PKT     接收数据包待处理中断允许位 
 *              EIE_FLAG_DMA     DMA 中断允许位 
 *              EIE_FLAG_LINK    连接状态改变中断允许位 
 *              EIE_FLAG_TX      发送中断允许位 
 *              EIE_FLAG_WOL     WOL 中断允许位 
 *              EIE_FLAG_TXER    发送错误中断允许位 
 *              EIE_FLAG_RXER    接收错误中断允许位 
 */
void ENC28J60_DisableINT(uint8_t flag)
{
    __ENC28J60_BFC(REG_NOBANK_EIE, flag & ~(EIE_FLAG_INTI));
}

/**
 * @brief 获取当前EIR寄存器中的中断标志位
 * 
 * @note 这个函数会在获取EIR寄存器中的值后清空EIR寄存器
 *       如果使能了INT引脚中断，则务必确保在中断发生后调用这个函数，
 *       否则INT引脚会一直保持为低电平，导致后续中断丢失，但是不建议
 *       在中断中直接调用，因为涉及到SPI操作，可能发生中断时正好在进
 *       行SPI操作，而SPI外设可不是“可重入”的。
 * 
 * @return uint8_t 返回所有生效的中断标志位，
 *                 其值可以为以下值之一，或者它们的或结果，
 *                 如果为这些值的或结果，说明有多个中断发生了
 *                  EIR_FLAG_PKT      
 *                  EIR_FLAG_DMA      
 *                  EIR_FLAG_LINK     
 *                  EIR_FLAG_TX       
 *                  EIR_FLAG_WOL      
 *                  EIR_FLAG_TXER     
 *                  EIR_FLAG_RXER     
 */
uint8_t ENC28J60_GetInterruptFlag(void)
{
    uint8_t irq = 0x00;

#if ENC28J60_ENABLE_INT
    /**
     * 如果使能了INT中断输出，则要暂时关闭它，这样可以防止读取
     * 中断标志位时到来的中断丢失，如果在读取中断标志位时有中断到来，
     * 则这个中断会在重新使能INT中断输出后立刻触发INT引脚上的一个下降沿
     */
    __ENC28J60_BFC(REG_NOBANK_EIE, EIE_FLAG_INTI);
#endif

    /* 读取INT引脚对应的中断标志位 */
    __ENC28J60_RCR_ETH(REG_NOBANK_EIR, &irq);

    /* 清除对应中断标志位 */
    __ENC28J60_BFC(REG_NOBANK_EIR, irq);

#if ENC28J60_ENABLE_INT
    /* 重新使能INT引脚的中断输出 */
    __ENC28J60_BFS(REG_NOBANK_EIE, EIE_FLAG_INTI);
#endif

    return irq;
}

#endif //ENC28J60_ENABLE_INT
